﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Xps.Packaging;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentManager.Abstractions.Interfaces.Helpers;
using GrigSoft.Managers.Abstractions.Interfaces.IO;
using Microsoft.Office.Interop.Word;
using Paragraph = DocumentFormat.OpenXml.Wordprocessing.Paragraph;
using Table = DocumentFormat.OpenXml.Wordprocessing.Table;

namespace DocumentManager.Helpers
{
	public class WordDocumentAdapter : IWordDocumentAdapter
	{
		#region Constants

		private const string DASH_SEPARATOR = "-";

		#endregion

		#region Variables

		private readonly IFileManager _fileManager;

		#endregion

		#region Constructors

		public WordDocumentAdapter(IFileManager fileManager)
		{
			if (fileManager == null)
				throw new ArgumentNullException(string.Format("file Manager"));
			_fileManager = fileManager;
		}

		#endregion

		#region IWordDocumentAdapter Members

		public string CreateTempDocumentByTemplate(string documentTemplateFilePath, string newDocumentFileName)
		{
			if (!File.Exists(documentTemplateFilePath))
				throw new FileNotFoundException(string.Format("file '{0}' not found!", documentTemplateFilePath));

			var templateExt = _fileManager.GetFileExtension(documentTemplateFilePath);
			var newFileName = string.Concat(newDocumentFileName, DASH_SEPARATOR, DateTime.Now.ToString("yyyy-MM-dd"), templateExt);
			var newFilePath = Path.Combine(Path.GetTempPath(), newFileName);

			return _fileManager.CopyFile(documentTemplateFilePath, newFilePath) ? newFilePath : string.Empty;
		}

		public void MergeDocumentData(string documentFilePath, IDictionary<string, string> data)
		{
			if (data == null || !data.Any()) return;

			if (!File.Exists(documentFilePath))
				throw new FileNotFoundException(string.Format("word document is not found"));

			// Open a WordprocessingDocument for editing using the filepath.
			var wordprocessingDocument = WordprocessingDocument.Open(documentFilePath, true);

			// Assign a reference to the existing document body.
			var body = wordprocessingDocument.MainDocumentPart.Document.Body;

			foreach (var item in data)
			{
				var elements = body.Elements<Paragraph>().Where(p => p.InnerText.Contains(item.Key));

				foreach (var element in elements)
				{
					var runs = element.Elements<Run>().Where(r => r.InnerText.Contains(item.Key));

					foreach (var run in runs)
					{
						var texts = run.Elements<Text>().Where(t => t.Text.Equals(item.Key));

						foreach (var text in texts)
						{
							text.Text = text.Text.Replace(item.Key, item.Value);
						}
					}
				}
			}

			// Close the handle explicitly.
			wordprocessingDocument.Close();
		}

		public void MergeDocumentTableData(string documentFilePath, IEnumerable<IDictionary<string, string>> data)
		{
			if (data == null || !data.Any()) return;

			if (!File.Exists(documentFilePath))
				throw new FileNotFoundException(string.Format("word document is not found"));

			// Open a WordprocessingDocument for editing using the filepath.
			var wordprocessingDocument = WordprocessingDocument.Open(documentFilePath, true);

			// Assign a reference to the existing document body.
			var body = wordprocessingDocument.MainDocumentPart.Document.Body;

			var table = body.Elements<Table>().FirstOrDefault();
			if (table != null)
			{
				var tableRow = table.Elements<TableRow>().LastOrDefault();
				if (tableRow != null)
				{
					foreach (var dataItem in data)
					{
						table.Append(tableRow.CloneNode(true));

						var updatedTableRow = table.Elements<TableRow>().LastOrDefault();
						if (updatedTableRow != null)
						{
							foreach (var dataItemElement in dataItem)
							{
								var cell = updatedTableRow.Elements<TableCell>()
									.FirstOrDefault(tc => tc.InnerText.Contains(dataItemElement.Key));
								if (cell != null)
								{
									var paragraph = cell.Elements<Paragraph>().FirstOrDefault(p => p.InnerText.Contains(dataItemElement.Key));
									if (paragraph != null)
									{
										var run = paragraph.Elements<Run>().FirstOrDefault(r => r.InnerText.Contains(dataItemElement.Key));
										if (run != null)
										{
											var text = run.Elements<Text>().FirstOrDefault(t => t.Text.Equals(dataItemElement.Key));
											if (text != null)
												text.Text = text.Text.Replace(dataItemElement.Key, dataItemElement.Value);
										}
									}
								}
							}
						}
					}
					table.RemoveChild(tableRow);
				}
			}

			// Close the handle explicitly.
			wordprocessingDocument.Close();
		}

		public XpsDocument ConvertWordDocumentToXPSDocument(string wordDocumentFilePath, string xpsDocumentFilePath)
		{
			var wordApplication = new Application();
			wordApplication.Documents.Add(wordDocumentFilePath);

			var document = wordApplication.ActiveDocument;
			try
			{
				document.SaveAs(xpsDocumentFilePath, WdSaveFormat.wdFormatXPS);
				wordApplication.Quit();

				var xpsDocument = new XpsDocument(xpsDocumentFilePath, FileAccess.Read);
				return xpsDocument;
			}
			catch (Exception)
			{
				return null;
			}
		}

		#endregion
	}
}
